package com.llbnk.service.impl;

import com.llbnk.dao.PromoDOMapper;
import com.llbnk.pojo.entity.PromoDO;
import com.llbnk.pojo.model.ItemModel;
import com.llbnk.pojo.model.PromoModel;
import com.llbnk.pojo.model.UserModel;
import com.llbnk.service.ItemService;
import com.llbnk.service.PromoService;
import com.llbnk.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author llbnk
 * @create 2022-08-22 14:55
 */
@Service
@Slf4j
public class PromoServiceImpl implements PromoService {

    @Resource
    private PromoDOMapper promoDOMapper;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private ItemService itemService;

    @Resource
    private UserService userService;

    @Override
    public PromoModel getPromoByItemId(Integer itemId) {
        PromoDO promoDO = promoDOMapper.selectByItemId(itemId);
        //entity->model
        PromoModel promoModel = convertFromEntity(promoDO);
        if(promoModel == null){
            return null;
        }

        //判断当前时间活动是否即将开始或正在进行
        if(promoModel.getStartDate().isAfterNow()){
            //活动未开始
            promoModel.setStatus(1);
        }else if (promoModel.getEndDate().isBeforeNow()){
            //活动已结束
            promoModel.setStatus(3);
        }else {
            //正在进行中
            promoModel.setStatus(2);
        }

        return promoModel;
    }

    /**相当于预先在秒杀活动未上线的时候，已经将活动库存放入redis中了*/
    @Override
    public void publishPromo(Integer promoId) {
        PromoDO promoDO = promoDOMapper.selectByPrimaryKey(promoId);
        if (promoDO.getItemId() == null || promoDO.getItemId().intValue() == 0)
            return;
        ItemModel itemModel = itemService.getItemById(promoDO.getItemId());
        //将库存同步到redis中区
        log.info("应放入redis中的stock数量为： "+itemModel.getStock());
        redisTemplate.opsForValue().set("promo_item_stock_" + itemModel.getId(),itemModel.getStock());

        //将大闸的限制数字设置到redis内
        //这个意思就是当前我有100个库存，那我就对应生成500个令牌，至于这500个令牌怎么分是由用户自己去抢
        redisTemplate.opsForValue().set("promo_door_count_"+promoId,itemModel.getStock().intValue()*5);
    }

    //生成秒杀令牌
    @Override
    public String generateSecondKillToken(Integer promoId, Integer itemId, Integer userId) {

        //判断是否库存已售空，如果售空就不颁发令牌了
        if(redisTemplate.hasKey("promo_item_stock_invalid_"+itemId)){
            return null;
        }

        PromoDO promoDO = promoDOMapper.selectByPrimaryKey(promoId);
        //dataobject->model
        PromoModel promoModel = convertFromEntity(promoDO);
        //如果promoModel不存在直接生成空令牌
        if(promoModel == null){
            return null;
        }

        //判断当前时间活动是否即将开始或正在进行
        if(promoModel.getStartDate().isAfterNow()){
            //活动未开始
            promoModel.setStatus(1);
        }else if (promoModel.getEndDate().isBeforeNow()){
            //活动已结束
            promoModel.setStatus(3);
        }else {
            //正在进行中
            promoModel.setStatus(2);
        }

        //预约活动状态 1表示未开始，2表示进行中，3表示以结束
        //只有status为2的时候才允许生成令牌
        if(promoModel.getStatus().intValue()!=2){
            return null;
        }
        //同样不但要校验活动状态，活动和用户也需要校验
        ItemModel itemModel = itemService.getItemByIdInCache(itemId);
        if (itemModel == null) {
            return null;
        }
        UserModel userModel = userService.getUserByIdInCache(userId);
        if (userModel == null) {
            return null;
        }

        //这个地方就是令牌大闸的闸门，我们预先准备了5倍库存的令牌
        //在这里没生成一个新的令牌就将大闸数量-1直到不发放令牌为止
        Long result = redisTemplate.opsForValue().increment("promo_door_count_" + promoId, -1);
        if(result < 0){
            return null;
        }

        //将生成的令牌放入redis中,5min有效期
        String token = UUID.randomUUID().toString().replace("-", "");
        redisTemplate.opsForValue().set("promo_token_"+promoId+"_userid_"+userId+"_itemid_"+itemId,token);
        redisTemplate.expire("promo_token_"+promoId+"_userid_"+userId+"_itemid_"+itemId,5, TimeUnit.MINUTES);
        return token;

    }

    private PromoModel convertFromEntity(PromoDO promoDO){
        if(promoDO == null){
            return null;
        }
        PromoModel promoModel = new PromoModel();
        BeanUtils.copyProperties(promoDO, promoModel);
        promoModel.setPromoItemPrice(BigDecimal.valueOf(promoDO.getPromoItemPrice()));
        promoModel.setStartDate(new DateTime(promoDO.getStartDate()));
        promoModel.setEndDate(new DateTime(promoDO.getEndDate()));
        return promoModel;
    }

}
